// 给定一个非负整数数组和一个整数 m，你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。

// 注意:
// 数组长度 n 满足以下条件:

// 1 ≤ n ≤ 1000
// 1 ≤ m ≤ min(50, n)
// 示例:

// 输入:
// nums = [7,2,5,10,8]
// m = 2

// 输出:
// 18

// 解释:
// 一共有四种方法将nums分割为2个子数组。
// 其中最好的方式是将其分为[7,2,5] 和 [10,8]，
// 因为此时这两个子数组各自的和的最大值为18，在所有情况中最小。

#include <vector>

using namespace std;

/* 二分查找+贪心
当我们选定一个值 x，我们可以线性地验证是否存在一种分割方案，满足其最大分割子数组和不超过 x.
二分的上界为数组 nums 中所有元素的和，下界为数组 nums 中所有元素的最大值。
时间复杂度：O(n * log(sum - maxN))
空间复杂度：O(1)
*/
class Solution {
public:
    int splitArray(vector<int>& nums, int m) {
        long long left{0}; // 数组 nums 中所有元素的最大值
        long long right{0}; // 数组 nums 中所有元素的和
        int n = nums.size();
        for (int i{0}; i < n; ++i) {
            right += nums[i];
            if (left < nums[i]) {
                left = nums[i];
            }
        }
        while (left < right) {
            long long mid = left + (right - left) / 2;
            if (check(nums, mid, m)) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
    bool check(const vector<int>& nums, int x, int m) {
        long long sum{0};
        int count{1};
        int n = nums.size();
        for (int i{0}; i < n; ++i) {
            if (sum + nums[i] > x) {
                ++count;
                sum = nums[i];
            } else {
                sum += nums[i];
            }
        }
        return count <= m;
    }
};